package com.jumi.microservice.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.jumi.microservice.common.constant.RedisKeyConstant;
import com.jumi.microservice.common.exception.BusinessException;
import com.jumi.microservice.common.redis.RedisCache;
import com.jumi.microservice.common.utils.EnumUtil;
import com.jumi.microservice.common.utils.IpUtil;
import com.jumi.microservice.common.utils.JwtUtil;
import com.jumi.microservice.config.UserTokenConfig;
import com.jumi.microservice.constant.*;
import com.jumi.microservice.domain.Account;
import com.jumi.microservice.domain.Department;
import com.jumi.microservice.domain.Log;
import com.jumi.microservice.domain.dto.UserLoginDTO;
import com.jumi.microservice.domain.dto.UserTokenDTO;
import com.jumi.microservice.mapper.AccountMapper;
import com.jumi.microservice.mapper.DepartmentMapper;
import com.jumi.microservice.mapper.LogMapper;
import com.jumi.microservice.service.UserLoginService;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * 账号登录管理Service组件
 *
 * @author Administrator
 */

@Service
public class UserLoginServiceImpl implements UserLoginService {

    /**
     * 账号管理mapper组件
     */
    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private UserTokenConfig userTokenConfig;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private LogMapper logMapper;

    @Autowired
    private DepartmentMapper departmentMapper;

    /**
     * 用户登录
     *
     * @param userLogin 用户登录VO
     * @return 用户TokenVo
     */
    @Override
    public UserTokenDTO login(UserLoginDTO userLogin, HttpServletRequest request) {
        ExceptionEnum.FAIL_NULL.doThrowIf(userLogin == null);

        String userAccount = userLogin.getAccount();
        LambdaQueryWrapper<Account> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(Account::getStatus, StatusEnum.STATUS_YES.getCode());
        wrapper.eq(Account::getIsDel, StatusEnum.STATUS_NO.getCode());
        wrapper.eq(StringUtils.isNotEmpty(userAccount), Account::getUserAccount, userAccount);

        Account account = accountMapper.selectOne(wrapper);
        if (account == null) {
            throw new BusinessException(UserAccountEnum.USER_NOT_EXISTED);
        }

        LoginTypeEnum loginTypeEnum = EnumUtil.getByCode(userLogin.getLoginType(), LoginTypeEnum.class);
        if (loginTypeEnum == LoginTypeEnum.PASSWORD_LOGIN) {
            //密码登录
            boolean res = checkPassword(userLogin.getPwdOrCode(), account.getSalt(), account.getPassword());
            if (!res) {
                throw new BusinessException(UserAccountEnum.PASSWORD_ERROR);
            }
        } else {
            //验证码登录
        }
        UserTokenDTO userToken = new UserTokenDTO();
        userToken.setUid(String.valueOf(account.getUid()));
        userToken.setToken(createToken(account.getUid(), account.getUserName()));

        //记录操作登录操作日志
        Log log = new Log();
        log.setAccountId(account.getUid());
        log.setUserName(account.getUserName());
        log.setMobile(account.getMobile());
        log.setDepartmentId(account.getDepartmentId());

        StringBuilder stringBuilder = new StringBuilder();
        String departmentName = getDepartmentName(account.getDepartmentId(), stringBuilder);
        log.setDepartmentName(departmentName);

        log.setOperateType(LogOperationTypeEnum.OPERATION_LOGIN.getCode());
        log.setOperateAction(LogOperationTypeEnum.OPERATION_LOGIN.getDesc());
        log.setIp(IpUtil.getClientIp(request));
        logMapper.insert(log);
        return userToken;
    }

    /**
     * 根据部门ID递归查询父部门名称和自己部门名称的拼串
     *
     * @param id 部门ID
     * @return 部门名称的拼串
     */
    private String getDepartmentName(Long id, StringBuilder stringBuilder) {
        Department department = departmentMapper.selectById(id);

        if (stringBuilder.length() == 0) {
            if (Objects.nonNull(department)) {
                stringBuilder.append(department.getDepartmentName());
            }
        } else {
            if (department.getParentId() != 0) {
                stringBuilder.insert(0, department.getDepartmentName() + "-");
            }
        }
        if (department.getParentId() != 0) {
            getDepartmentName(department.getParentId(), stringBuilder);
        }
        return stringBuilder.toString();
    }
    /**
     * 用户退出
     *
     * @param uid
     * @return
     */
    @Override
    public boolean logout(Long uid) {
        String key = RedisKeyConstant.USER_TOKEN_KEY + uid;
        return redisCache.deleteObject(key);
    }

    /**
     * 重置密码
     *
     * @param password 密码
     * @return
     */
    @Override
    public boolean resetPwd(String uid, String password) {
        LambdaQueryWrapper<Account> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(Account::getStatus, StatusEnum.STATUS_YES.getCode());
        wrapper.eq(Account::getIsDel, StatusEnum.STATUS_NO.getCode());
        wrapper.eq(StringUtils.isNotEmpty(uid), Account::getUid, uid);

        Account account = accountMapper.selectOne(wrapper);
        if (account == null) {
            throw new BusinessException(UserAccountEnum.USER_NOT_EXISTED);
        }

        if (!StringUtils.isBlank(password)) {
            String salt = generateSalt();
            String newPwd = DigestUtils.md5DigestAsHex(password.concat(salt).getBytes());
            account.setSalt(salt);
            account.setPassword(newPwd);

            account.setModifier(account.getUserName());
            account.setUpdateTime(LocalDateTime.now());
        }
        int i = accountMapper.updateById(account);
        return i > 0;
    }

    /**
     * 个人中心基本资料修改
     *
     * @param uid        用户UID
     * @param mobile     手机号码
     * @param userAvatar 用户头像
     * @return 处理结果, 修改成功返回true
     */
    @Override
    public boolean updatePersonalBasicData(Long uid, String mobile, String userAvatar) {
        LambdaQueryWrapper<Account> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(Account::getStatus, StatusEnum.STATUS_YES.getCode());
        wrapper.eq(Account::getIsDel, StatusEnum.STATUS_NO.getCode());
        wrapper.eq(Account::getUid, uid);

        Account account = accountMapper.selectOne(wrapper);
        if (account == null) {
            throw new BusinessException(UserAccountEnum.USER_NOT_EXISTED);
        }

        List<Object> objectList = accountMapper.selectObjs(
                Wrappers.<Account>lambdaQuery()
                        .select(Account::getId)
                        .eq(Account::getMobile, mobile)
                        .eq(Account::getIsDel, StatusEnum.STATUS_NO.getCode())
                        .ne(account.getId() != null, Account::getId, account.getId()));

        if (objectList.size() > NumberConstant.NUMBER_ZERO.intValue()) {
            throw new BusinessException(UserAccountEnum.PHONE_HAS_EXISTED);
        }

        account.setMobile(mobile);
        account.setUserAvatar(userAvatar);
        account.setModifier(account.getUserName());
        account.setUpdateTime(LocalDateTime.now());

        return accountMapper.updateById(account) > NumberConstant.NUMBER_ZERO.intValue();
    }

    /**
     * 随即生成盐
     *
     * @return salt 盐
     */
    private String generateSalt() {
        return RandomStringUtils.randomAlphanumeric(20);
    }

    /**
     * 密码校验
     *
     * @param password 明文
     * @param salt     盐
     * @param hashPwd  密文
     * @return
     */
    private boolean checkPassword(String password, String salt, String hashPwd) {
        return hashPwd.equals(DigestUtils.md5DigestAsHex(password.concat(salt).getBytes()));
    }

    /**
     * 生成user token
     *
     * @param uid 用户账号ID
     * @return
     */
    private String createToken(Long uid, String userName) {
        String token = JwtUtil.createToken(uid, userName, userTokenConfig.getExpiredDays());
        String key = RedisKeyConstant.USER_TOKEN_KEY + uid;
        redisCache.setCacheObject(key, token, userTokenConfig.getExpiredDays(), TimeUnit.DAYS);
        return token;
    }
}
